/*
 * scumach.c, machine architecture reader for SmartCam skeleton system
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <log.h>

#include "scumach.h"

extern log_default_streams_t scul_mach;

/// Scrubs extraneous whitespace and comments from a configuration line.
int scuMachScrubLine(char *line);
/// Interpret an item=value configuration pair.
void scuMachInterpretItem(scu_mach_t ***m, char *section, char *item, char *value);

scu_mach_t **scuMachReadFile(char *name)
{
  scu_mach_t **m;
#ifdef PSOS_
  /* Quick test */
  abortOnNULL(STREAM(scul_mach.err), m=(scu_mach_t**)malloc(2*sizeof(scu_mach_t*)));

  abortOnNULL(STREAM(scul_mach.err), m[0] = (scu_mach_t*)malloc(sizeof(scu_mach_t)));
  abortOnNULL(STREAM(scul_mach.err), m[0]->name = malloc(sizeof("TriMedia")+1));
  strcpy(m[0]->name, "TriMedia");
  m[0]->address.sa_family = AF_UNIX;
  strcpy(m[0]->address.sa_data, "/proc/tm1");
  m[0]->addrlen = strlen("/proc/tm1") + sizeof(m[0]->address.sa_family);

/*
  abortOnNULL(STREAM(scul_mach.err), m[1] = (scu_mach_t*)malloc(sizeof(scu_mach_t)));
  m[1]->name = "Xetal";
  m[1]->address.sa_family = AF_UNIX;
  strcpy(m[1]->address.sa_data, "/proc/tm1");
  m[1]->addrlen = strlen("/proc/tm1") + sizeof(m[0]->address.sa_family);
*/
  m[1] = NULL;
#else
  FILE *file;
  char line[PATH_MAX], section[PATH_MAX], item[PATH_MAX], value[PATH_MAX];

  abortOnNULL(STREAM(scul_mach.err), m=(scu_mach_t**)malloc(sizeof(scu_mach_t*)));
  m[0] = NULL;

  abortOnNULL(STREAM(scul_mach.err), file=fopen(name, "r"));

  while (fgets(line, PATH_MAX-1, file), !feof(file))
  {
    if (scuMachScrubLine(line))
    {
      if (strchr(line, '[') == line &&
	  strchr(line, ']') == &line[strlen(line)-1])
      {
	/* New section */
        strcpy(section, &line[1]);
	section[strlen(line)-2] = '\0';
      }
      else if (strchr(line, -1))
      {
        /* Item=value pair */
	strcpy(item, line);
        *strchr(item, -1) = '\0';
        strcpy(value, strchr(line, -1)+1);
        scuMachInterpretItem(&m, section, item, value);
      }
      else
      {
	/* Unknown */
        lprintf(STREAM(scul_mach.err), line);
	abortErrorl((STREAM(scul_mach.err), "Unrecognised configuration line"));
      }
    }
  }
#endif

  return m;
}

void scuMachFree(scu_mach_t ***m)
{
  int i;

  for (i=0; (*m)[i]; i++)
  {
    free((*m)[i]->name);
    free((*m)[i]);
  }
  free(*m);

  *m = NULL;
}

/**
 * \param line configuration line.
 * \retval 0 no data on this line.
 * \retval other scrubbed data available.
 */
int scuMachScrubLine(char *line)
{
  char s[PATH_MAX];
  int i, j;
  int stringlevel=0, stringchar=0;
  int comment=0;
  
  strcpy(s, line);
  
  /* Go through the line */
  for (i=0, j=0; i < strlen(s); i++)
  {
    if (!comment)
    {
      if (!stringlevel)
      {
	/* No comment, no string */
	switch (s[i])
	{
	  case '\'':
	  case '\"':
	    /* String starting */
	    stringlevel++;
	    stringchar = s[i];
	    break;
	  case '#':
	    /* Comment starting */
	    comment = 1;
	    break;
	  case '=':
	    /* Item separator */
	    line[j++] = -1;
	    break;
	  case '\t':
          case ' ':
          case '\n':
	    /* Whitespace */
	    break;
	  default: 
	    line[j++] = s[i];
	    break;
	}
      }
      else
      {
	/* We're inside a string, don't strip whitespace here */
	if (s[i] == stringchar)
	{
	  /* End of string */
	  stringlevel--;
	  if (stringlevel)
	  {
	    line[j++] = s[i];
	    switch (stringchar)
	    {
	      case '\'': stringchar = '\"'; break;
	      case '\"': stringchar = '\''; break;
	    }
	  }
	}
	else
	{
	  switch (s[i])
	  {
	    case '\'':
	    case '\"':
	      /* New string in this string */
	      stringlevel++;
	      line[j++] = s[i];
	      stringchar = s[i];
	      break;
	    default:
	      line[j++] = s[i];
	      break;
	  }
	}
      }
    }
  }
  
  line[j] = '\0';

  return (line[0] != '\0');
}

void scuMachInterpretItem(scu_mach_t ***m, char *section, char *item, char *value)
{
  int i;

  lprintf(STREAM(scul_mach.debug), "item: [%s] '%s'='%s'", section, item, value);

  if (!strcmp(section, "hosts"))
  {
    /* Find free spot */
    for (i=0; (*m)[i]; i++);

    /* Reserve memory */
    abortOnNULL(STREAM(scul_mach.err),
		*m = (scu_mach_t**)realloc(*m, (i+2)*sizeof(scu_mach_t*)));
      
    abortOnNULL(STREAM(scul_mach.err),
		(*m)[i] = (scu_mach_t*)malloc(sizeof(scu_mach_t)));
    
    (*m)[i+1] = NULL;

    /* Fill new spot */
    abortOnNULL(STREAM(scul_mach.err),
		(*m)[i]->name = (char*)malloc((strlen(item)+1)*sizeof(unsigned char)));
    strcpy((*m)[i]->name, item);
    memset(&(*m)[i]->address, 0, sizeof((*m)[i]->address));
    (*m)[i]->type = SCU_PROC_MAX;

    lprintf(STREAM(scul_mach.info), "Added processor '%s'", (*m)[i]->name);
  }
  else
  {
    for (i=0; (*m)[i]; i++)
    {
      if (!strcmp((*m)[i]->name, section))
      {
        if (!strcmp(item, "type"))
        {
          if (!strcmp(value, "ilp"))
          {
            (*m)[i]->type = SCU_PROC_ILP;
            lprintf(STREAM(scul_mach.info), "%s is an %s processor", (*m)[i]->name, value);
          }
          else if (!strcmp(value, "xtl"))
          {
            (*m)[i]->type = SCU_PROC_XTL;
            lprintf(STREAM(scul_mach.info), "%s is an %s processor", (*m)[i]->name, value);
          }
          else
            lprintf(STREAM(scul_mach.warning), "Unknown type '%s' in section for processor '%s'", value, section);
        }
        else if (!strcmp(item, "ip"))
        {
          (*m)[i]->address.sa_family = AF_INET;
          (*m)[i]->addrlen = sizeof((*m)[i]->address);
#ifndef PSOS
          abortOnNULL(STREAM(scul_mach.err), ((struct sockaddr_in*)(&(*m)[i]->address))->sin_addr.s_addr = inet_addr(value));
#endif
          lprintf(STREAM(scul_mach.info), "%s has IP address %s", (*m)[i]->name, value);
        }
        else if (!strcmp(item, "unix"))
        {
          (*m)[i]->address.sa_family = AF_UNIX;
          (*m)[i]->addrlen = strlen(value) + sizeof((*m)[i]->address.sa_family);
          strcpy((*m)[i]->address.sa_data, value);

          lprintf(STREAM(scul_mach.info), "%s has UNIX address %s", (*m)[i]->name, value);
        }
        else
        {
          lprintf(STREAM(scul_mach.warning), "Unknown item '%s' in section for processor '%s'", item, section);
        }
      }
    }
  }
}
